package code.extract;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import util.StringDealer;


/**
 * @author Fang Lu, fanglupku@gmail.com
 */

public class CodeMerger {
//	public static void main(String args[]){
//	}
	public static final int NO_CODE = 0;
	public static final int BEGIN_CODE = 1 << 1;
	public static final int END_CODE = 1 << 2;
	public static final int BEGIN_COMMENT = 1 << 3;
	public static final int END_COMMENT = 1 << 4;
	public static final int TOTAL_CODE = 1 << 5;
	public static final int EMPTY_ITEM = 1 << 6;
//	public static final int INCLUDE_CODE = 1 << 3;
	
	private static final int TEXT_TOKEN_THRESHOLD = 22;
	
	public static boolean simpleJudgeCode(String line){
		line = line.trim();
		boolean isCode = false;
		StringTokenizer st = new StringTokenizer(line, " \t\n\r\f(){}[];=+-*/&!~");
		for (String s:CodeJudge.mustOccurSymbol){
			if(line.contains(s)){
				return true;
			}
		}
		for (String s:CodeJudge.codeEndSymbol){
			if (line.endsWith(s)){
				return true;
			}
		}
		while (st.hasMoreTokens()){
			String thisToken = st.nextToken();
			for (String s:CodeJudge.codeKeywordSymbol){
				if (thisToken.equals(s)){
					return true;
				}
			}
		}
		
		
		return isCode;
	}
	public static String eraseComment(String text){
		String strPattern = "/\\u002A.*?\\u002A/";
		Pattern pattern = Pattern.compile(strPattern, Pattern.DOTALL);
		Matcher matcher = pattern.matcher(text);
		String result = matcher.replaceAll("");
		strPattern = "//.*";
		pattern = Pattern.compile(strPattern);
		matcher = pattern.matcher(result);
		result = matcher.replaceAll("");
		return result;
	}
	public static int computeCodeStatu(String text){
		int result = NO_CODE;
		boolean isFirstLine = true;
		int codeLine = 0;
		int totalLine = 0;
		int tokenCount = 0;
		int speratorCount = 0;
		String textNoComment = eraseComment(text);
		for (int i=0; i<textNoComment.length(); ++i){
			if (textNoComment.charAt(i) == ';' || textNoComment.charAt(i) == '}'
				|| textNoComment.charAt(i) == '{' || textNoComment.charAt(i) == '('
				|| textNoComment.charAt(i) == ')' || textNoComment.charAt(i) == '='){
				++ speratorCount;
			}
		}
		codeLine = StringDealer.getLineNumber(text) - StringDealer.getLineNumber(textNoComment);
		StringReader sr = new StringReader(textNoComment);
		BufferedReader br = new BufferedReader(sr);
		
		try {
			String line = br.readLine();
			boolean isCode = false;
			if (textNoComment.length() != text.length()){
				isCode = true;
			}
			while (line != null){
				line = line.trim();
				if (line.length() > 0){
					isCode = simpleJudgeCode(line);
					++ totalLine;
					if (isCode){
//						result = result | INCLUDE_CODE;
						++ codeLine;
					}
					if (isFirstLine && isCode){
						result = result | BEGIN_CODE;
					}
					isFirstLine = false;
				}
				tokenCount += StringDealer.countTokens(line);
				line = br.readLine();
			}
			double codeRatio = (double)codeLine / (double)totalLine;
			if (codeRatio > 0.5){
				result = result | TOTAL_CODE;
			}
			if (isCode){
				result = result | END_CODE;
				if (isFirstLine == true){
					result = result | BEGIN_CODE;
				}
			}
			if (codeLine == 1){
				if (speratorCount == 0
						|| (tokenCount) / speratorCount > TEXT_TOKEN_THRESHOLD){
					result = NO_CODE;
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return result;
	}
	public static boolean isContinualCode(SegmentItem segFirst, SegmentItem segSecond){
		int firstStatu = computeCodeStatu(segFirst.getContent());
		int secondStatu = computeCodeStatu(segSecond.getContent());
//		System.out.println("++:" + firstStatu + "/" + secondStatu);
		if ((firstStatu & END_CODE) !=0 && (secondStatu & BEGIN_CODE) != 0){
//				&& (firstStatu & TOTAL_CODE) != 0 && (secondStatu & TOTAL_CODE )!= 0){
			return true;
		}
		return false;
	}
	public static boolean isEmptyString(String input){
		if (input == null 
				|| input.replaceAll("\\s", "").trim().length() == 0){
			return true;
		}else{
			return false;
		}
	}
	/**
	 * this is a simple implements of merge, 
	 * 	when the adjacent two texts are all code.
	 * @return List after merge
	 */
	public static ArrayList<SegmentItem> continualCodeMerge(ArrayList<SegmentItem> srcList){
		ArrayList<SegmentItem> continualCommentList = continualCommentMerge(srcList);
//		ArrayList<SegmentItem> continualCommentList = srcList;
		ArrayList<SegmentItem> result = new ArrayList<SegmentItem>();
		SegmentItem thisItem = null;
		
		for (int i=0; i<continualCommentList.size(); ){
			int beginIndex =  i;
			thisItem = continualCommentList.get(i);
			StringBuffer sb = new StringBuffer();
			sb.append(continualCommentList.get(i).getContent());
			++ i;
			int ignoreEmptyCount = 0;
			while (i < continualCommentList.size()){
				SegmentItem nextItem = continualCommentList.get(i);
				if (isEmptyString(nextItem.getContent()) == true){
					++ i;
					++ ignoreEmptyCount;
					continue;
				}
				boolean doMerge = isContinualCode(thisItem, nextItem);
				if (doMerge){
					ignoreEmptyCount = 0;
					sb.append("\n");
					sb.append(nextItem.getContent());
				}else{
					break;
				}
				thisItem = nextItem;
				++ i;
			}
			i = i - ignoreEmptyCount;
			int endIndex = i - 1;
			if (endIndex >= continualCommentList.size()){
				endIndex = continualCommentList.size() - 1;
			}
			SegmentItem newItem = new SegmentItem(continualCommentList.get(beginIndex));
			newItem.setContent(sb.toString());
			newItem.setEndTag(continualCommentList.get(endIndex).getEndTag());
			result.add(newItem);
		}
		return result;
	}
	public static int computeCommentStat(SegmentItem thisItem){
		String content = thisItem.getContent();
		if (content == null 
				|| content.replaceAll("\\s", "").trim().length() == 0){
			return EMPTY_ITEM;
		}
		int beginIndex = 0;
		int endIndex = 0;
		int result = 0;
		while ((beginIndex = content.indexOf("/*", endIndex)) > -1){
			beginIndex += 2;
			result = result | BEGIN_COMMENT;
			if ((endIndex = content.indexOf("*/", beginIndex)) > -1){
				result = result | END_COMMENT;
				endIndex += 2;
			}
			else{
				result = result & (~END_COMMENT);
				break;
			}
		}
		if ((result & BEGIN_COMMENT) == 0){
			if ((endIndex = content.indexOf("*/")) > -1){
				result = result | END_COMMENT;
			}
		}
		return result;
	}
	public static ArrayList<SegmentItem> continualCommentMerge(ArrayList<SegmentItem> srcList){
		ArrayList<SegmentItem> result = new ArrayList<SegmentItem>();
		int srcIndex = 0;
		
		for (srcIndex = 0; srcIndex < srcList.size(); ){
			int thisCommentStatu = computeCommentStat(srcList.get(srcIndex));
			if (((thisCommentStatu & BEGIN_COMMENT) != 0) && ((thisCommentStatu & END_COMMENT) == 0)){
				int beginIdex = srcIndex;
//				int endIndex = srcIndex;
				StringBuffer sb = new StringBuffer();
				sb.append(srcList.get(srcIndex).getContent());
				++ srcIndex;
				int ignoreEmptyCount = 0;
				while (srcIndex < srcList.size()){
					thisCommentStatu = computeCommentStat(srcList.get(srcIndex));
					if ((thisCommentStatu & EMPTY_ITEM) != 0){
						++ srcIndex;
						++ ignoreEmptyCount;
						continue;
					}
					ignoreEmptyCount = 0;
					sb.append("\n");
					sb.append(srcList.get(srcIndex).getContent());
					if ((thisCommentStatu & END_COMMENT) != 0){
						break;
					}
					++ srcIndex;
				}
				srcIndex = srcIndex - ignoreEmptyCount;
				if (srcIndex >= srcList.size()){
					srcIndex = srcList.size() - 1;
				}
				SegmentItem newItem = new SegmentItem(srcList.get(beginIdex));
				newItem.setContent(sb.toString());
				newItem.setEndTag(srcList.get(srcIndex).getEndTag());
				result.add(newItem);
				++ srcIndex;
			}
			else{
				SegmentItem newItem = new SegmentItem(srcList.get(srcIndex));
				result.add(newItem);
				++ srcIndex;
			}
		}
		return result;
	}
	/**
	 * merge small code such as "<code> import </code> java.io.*;" together
	 * @param srcList
	 * @return
	 */
	public static ArrayList<SegmentItem> smallCodeMerge(ArrayList<SegmentItem> srcList){
		ArrayList<SegmentItem> result = new ArrayList<SegmentItem>();
		for (int i=0; i<srcList.size(); ++i){
			SegmentItem thisItem = srcList.get(i);
			String beginTagName = BasePageDealer.getTagName(thisItem.getBeginTag()).toLowerCase();
			String endTagName = BasePageDealer.getTagName(thisItem.getEndTag()).toLowerCase();
			String thisContent = thisItem.getContent().trim();
			int tokenNum = 0;
			StringTokenizer st = new StringTokenizer(thisContent);
			while (st.hasMoreTokens()){
				st.nextToken();
				++ tokenNum;
				
			}
//			System.out.println("token:" + tokenNum);
//			System.out.println(beginTagName + ":" + endTagName);
//			System.out.println(thisItem.getBeginTag() + ":" + thisItem.getEndTag());
//			System.out.println(thisItem.getContent());
			if (beginTagName.equals("code") && thisItem.getBeginTag().indexOf("</") < 0
					&& endTagName.equals("code") && thisItem.getEndTag().indexOf("</") >= 0
					&& tokenNum < 3){
				if (result.size() > 0 && result.get(result.size() - 1).isCodeSegment() == false){
					SegmentItem lastSeg = result.remove(result.size() - 1);
					SegmentItem mergedSeg = SegmentItem.mergeTwoSegment(lastSeg, thisItem);
					if ( i< srcList.size() - 1 && srcList.get(i+1).isCodeSegment() == false){
						mergedSeg = SegmentItem.mergeTwoSegment(mergedSeg, srcList.get(i+1));
						++ i;
					}
					result.add(mergedSeg);
				}else{
					SegmentItem mergedSeg = srcList.get(i);
					if ( i< srcList.size() - 1 && srcList.get(i+1).isCodeSegment() == false){
						mergedSeg = SegmentItem.mergeTwoSegment(mergedSeg, srcList.get(i+1));
						++ i;
					}
					result.add(mergedSeg);
				}
			}else{
				result.add(thisItem);
			}
		}
		
		return result;
	}
}
